Skip to content

Commit add0f57

Browse files
CopilotKSemenenko
andcommitted
Add .NET 9 Aspire project template with API, tests, and GitHub workflows
Co-authored-by: KSemenenko <4385716+KSemenenko@users.noreply.github.com>
1 parent db3ae16 commit add0f57

21 files changed

+847
-0
lines changed

.github/workflows/ci.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
push:
7+
branches: [ main ]
8+
9+
env:
10+
DOTNET_VERSION: '9.0.x'
11+
12+
jobs:
13+
build:
14+
name: Build and Test
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v5
20+
21+
- name: Setup .NET
22+
uses: actions/setup-dotnet@v4
23+
with:
24+
dotnet-version: ${{ env.DOTNET_VERSION }}
25+
26+
- name: Install Aspire workload
27+
run: dotnet workload install aspire
28+
29+
- name: Restore dependencies
30+
run: dotnet restore
31+
32+
- name: Build
33+
run: dotnet build --configuration Release --no-restore
34+
35+
- name: Test
36+
run: dotnet test --configuration Release --no-build --verbosity normal --collect:"XPlat Code Coverage"
37+
38+
- name: Upload coverage reports to Codecov
39+
uses: codecov/codecov-action@v5
40+
with:
41+
token: ${{ secrets.CODECOV_TOKEN }}
42+
files: ./**/coverage.cobertura.xml
43+
fail_ci_if_error: false
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: "CodeQL"
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
schedule:
9+
- cron: '0 0 * * 1'
10+
11+
env:
12+
DOTNET_VERSION: '9.0.x'
13+
14+
jobs:
15+
analyze:
16+
name: Analyze
17+
runs-on: ubuntu-latest
18+
permissions:
19+
actions: read
20+
contents: read
21+
security-events: write
22+
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
language: [ 'csharp' ]
27+
28+
steps:
29+
- name: Checkout repository
30+
uses: actions/checkout@v5
31+
32+
- name: Setup .NET
33+
uses: actions/setup-dotnet@v4
34+
with:
35+
dotnet-version: ${{ env.DOTNET_VERSION }}
36+
37+
- name: Install Aspire workload
38+
run: dotnet workload install aspire
39+
40+
- name: Initialize CodeQL
41+
uses: github/codeql-action/init@v4
42+
with:
43+
languages: ${{ matrix.language }}
44+
45+
- name: Restore dependencies
46+
run: dotnet restore
47+
48+
- name: Build
49+
run: dotnet build --no-restore
50+
51+
- name: Perform CodeQL Analysis
52+
uses: github/codeql-action/analyze@v4

.github/workflows/release.yml

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
workflow_dispatch:
7+
8+
env:
9+
DOTNET_VERSION: '9.0.x'
10+
11+
jobs:
12+
build:
13+
name: Build and Test
14+
runs-on: ubuntu-latest
15+
16+
outputs:
17+
version: ${{ steps.version.outputs.version }}
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v5
22+
23+
- name: Setup .NET
24+
uses: actions/setup-dotnet@v4
25+
with:
26+
dotnet-version: ${{ env.DOTNET_VERSION }}
27+
28+
- name: Install Aspire workload
29+
run: dotnet workload install aspire
30+
31+
- name: Extract version from Directory.Build.props
32+
id: version
33+
run: |
34+
VERSION=$(grep -oPm1 "(?<=<Version>)[^<]+" Directory.Build.props)
35+
echo "version=$VERSION" >> $GITHUB_OUTPUT
36+
echo "Version from Directory.Build.props: $VERSION"
37+
38+
- name: Restore dependencies
39+
run: dotnet restore
40+
41+
- name: Build
42+
run: dotnet build --configuration Release --no-restore
43+
44+
- name: Test
45+
run: dotnet test --configuration Release --no-build --verbosity normal
46+
47+
- name: Pack NuGet packages
48+
run: dotnet pack --configuration Release --no-build --output ./artifacts
49+
50+
- name: Upload artifacts
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: nuget-packages
54+
path: ./artifacts/*.nupkg
55+
retention-days: 5
56+
57+
publish-nuget:
58+
name: Publish to NuGet
59+
needs: build
60+
runs-on: ubuntu-latest
61+
if: github.ref == 'refs/heads/main'
62+
63+
outputs:
64+
published: ${{ steps.publish.outputs.published }}
65+
version: ${{ needs.build.outputs.version }}
66+
67+
steps:
68+
- name: Checkout
69+
uses: actions/checkout@v5
70+
71+
- name: Download artifacts
72+
uses: actions/download-artifact@v5
73+
with:
74+
name: nuget-packages
75+
path: ./artifacts
76+
77+
- name: Setup .NET
78+
uses: actions/setup-dotnet@v4
79+
with:
80+
dotnet-version: ${{ env.DOTNET_VERSION }}
81+
82+
- name: Publish to NuGet
83+
id: publish
84+
continue-on-error: true
85+
run: |
86+
set +e
87+
OUTPUT=""
88+
PUBLISHED=false
89+
90+
for package in ./artifacts/*.nupkg; do
91+
echo "Publishing $package..."
92+
RESULT=$(dotnet nuget push "$package" \
93+
--api-key ${{ secrets.NUGET_API_KEY }} \
94+
--source https://api.nuget.org/v3/index.json \
95+
--skip-duplicate 2>&1)
96+
EXIT_CODE=$?
97+
echo "$RESULT"
98+
OUTPUT="$OUTPUT$RESULT"
99+
100+
if [ $EXIT_CODE -eq 0 ]; then
101+
echo "Successfully published $package"
102+
PUBLISHED=true
103+
elif echo "$RESULT" | grep -q "already exists"; then
104+
echo "Package already exists, skipping..."
105+
else
106+
echo "Failed to publish $package"
107+
exit 1
108+
fi
109+
done
110+
111+
# Check if at least one package was successfully published
112+
if [ "$PUBLISHED" = true ] || echo "$OUTPUT" | grep -q "Your package was pushed"; then
113+
echo "published=true" >> $GITHUB_OUTPUT
114+
echo "At least one package was successfully published"
115+
else
116+
echo "published=false" >> $GITHUB_OUTPUT
117+
echo "No new packages were published (all already exist)"
118+
fi
119+
120+
create-release:
121+
name: Create GitHub Release and Tag
122+
needs: publish-nuget
123+
runs-on: ubuntu-latest
124+
if: needs.publish-nuget.outputs.published == 'true'
125+
126+
steps:
127+
- name: Checkout
128+
uses: actions/checkout@v5
129+
with:
130+
fetch-depth: 0
131+
token: ${{ secrets.GITHUB_TOKEN }}
132+
133+
- name: Download artifacts
134+
uses: actions/download-artifact@v5
135+
with:
136+
name: nuget-packages
137+
path: ./artifacts
138+
139+
- name: Create and push tag
140+
id: create_tag
141+
run: |
142+
VERSION="${{ needs.publish-nuget.outputs.version }}"
143+
TAG="v$VERSION"
144+
145+
# Configure git
146+
git config user.name "github-actions[bot]"
147+
git config user.email "github-actions[bot]@users.noreply.github.com"
148+
149+
# Check if tag already exists
150+
if git rev-parse "$TAG" >/dev/null 2>&1; then
151+
echo "Tag $TAG already exists"
152+
echo "tag_exists=true" >> $GITHUB_OUTPUT
153+
else
154+
echo "Creating tag $TAG"
155+
git tag -a "$TAG" -m "Release $VERSION"
156+
git push origin "$TAG"
157+
echo "tag_exists=false" >> $GITHUB_OUTPUT
158+
fi
159+
160+
- name: Get previous tag
161+
id: prev_tag
162+
run: |
163+
CURRENT_TAG="v${{ needs.publish-nuget.outputs.version }}"
164+
# Get the second most recent tag (previous to current)
165+
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^$CURRENT_TAG$" | tail -n1 || echo "")
166+
if [ "$PREVIOUS_TAG" = "$CURRENT_TAG" ] || [ -z "$PREVIOUS_TAG" ]; then
167+
# If no previous tag found, get the most recent tag before current
168+
PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -v "^$CURRENT_TAG$" | head -n1 || echo "")
169+
fi
170+
echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
171+
echo "Current tag: $CURRENT_TAG"
172+
echo "Previous tag: $PREVIOUS_TAG"
173+
174+
- name: Generate release notes
175+
id: release_notes
176+
run: |
177+
VERSION="${{ needs.publish-nuget.outputs.version }}"
178+
CURRENT_TAG="v$VERSION"
179+
PREVIOUS_TAG="${{ steps.prev_tag.outputs.previous_tag }}"
180+
181+
echo "# Release $VERSION" > release_notes.md
182+
echo "" >> release_notes.md
183+
echo "Released on $(date +'%Y-%m-%d')" >> release_notes.md
184+
echo "" >> release_notes.md
185+
186+
if [ -n "$PREVIOUS_TAG" ]; then
187+
echo "## 📋 Changes since $PREVIOUS_TAG" >> release_notes.md
188+
echo "" >> release_notes.md
189+
190+
# Group commits by type
191+
echo "### ✨ Features" >> release_notes.md
192+
git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --grep="^feat" --grep="^feature" >> release_notes.md || true
193+
echo "" >> release_notes.md
194+
195+
echo "### 🐛 Bug Fixes" >> release_notes.md
196+
git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --grep="^fix" --grep="^bugfix" >> release_notes.md || true
197+
echo "" >> release_notes.md
198+
199+
echo "### 📚 Documentation" >> release_notes.md
200+
git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --grep="^docs" --grep="^doc" >> release_notes.md || true
201+
echo "" >> release_notes.md
202+
203+
echo "### 🔧 Other Changes" >> release_notes.md
204+
git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..HEAD --invert-grep --grep="^feat" --grep="^feature" --grep="^fix" --grep="^bugfix" --grep="^docs" --grep="^doc" >> release_notes.md || true
205+
echo "" >> release_notes.md
206+
else
207+
echo "## 🎉 Initial Release" >> release_notes.md
208+
echo "" >> release_notes.md
209+
echo "### Recent Changes" >> release_notes.md
210+
git log --pretty=format:"- %s (%h)" --max-count=20 >> release_notes.md
211+
echo "" >> release_notes.md
212+
fi
213+
214+
echo "" >> release_notes.md
215+
echo "## 📦 NuGet Packages" >> release_notes.md
216+
echo "" >> release_notes.md
217+
for package in ./artifacts/*.nupkg; do
218+
PACKAGE_NAME=$(basename "$package" .nupkg)
219+
# Extract package name without version
220+
BASE_NAME=$(echo "$PACKAGE_NAME" | sed "s/\.$VERSION//")
221+
echo "- [$BASE_NAME v$VERSION](https://www.nuget.org/packages/$BASE_NAME/$VERSION)" >> release_notes.md
222+
done
223+
224+
echo "" >> release_notes.md
225+
echo "---" >> release_notes.md
226+
echo "*This release was automatically created by GitHub Actions*" >> release_notes.md
227+
228+
- name: Create GitHub Release
229+
uses: softprops/action-gh-release@v2
230+
with:
231+
tag_name: v${{ needs.publish-nuget.outputs.version }}
232+
name: v${{ needs.publish-nuget.outputs.version }}
233+
body_path: release_notes.md
234+
draft: false
235+
prerelease: false
236+
files: ./artifacts/*.nupkg
237+
token: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,8 @@ FodyWeavers.xsd
416416
*.msix
417417
*.msm
418418
*.msp
419+
420+
# Aspire
421+
.aspire/
422+
*.dcplog
423+
*.dcp.log

Directory.Build.props

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project>
2+
<PropertyGroup>
3+
<Version>1.0.0</Version>
4+
<Authors>ManagedCode</Authors>
5+
<Company>ManagedCode</Company>
6+
<Copyright>Copyright © ManagedCode $(Year)</Copyright>
7+
<PackageProjectUrl>https://github.com/managedcode/ProjectTemplate</PackageProjectUrl>
8+
<RepositoryUrl>https://github.com/managedcode/ProjectTemplate</RepositoryUrl>
9+
<RepositoryType>git</RepositoryType>
10+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
11+
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
12+
<PackageReadmeFile>README.md</PackageReadmeFile>
13+
<PackageTags>aspire;template;dotnet</PackageTags>
14+
</PropertyGroup>
15+
16+
<ItemGroup>
17+
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
18+
</ItemGroup>
19+
</Project>

0 commit comments

Comments
 (0)